package config

import (
	"reflect"
	"strconv"
	"strings"

	"gitee.com/go-mao/mao/libs/try"
)

// 配置类型
const (
	CONFIG_MODE_GROUP       = "group"       //分组
	CONFIG_MODE_LIST        = "list"        //列表
	CONFIG_MODE_JUDGE       = "judge"       //判断
	CONFIG_MODE_SGINGLE     = "single"      //单选
	CONFIG_MODE_MULTI       = "multi"       //多选
	CONFIG_MODE_TEXTAREA    = "text"        //多行文本
	CONFIG_MODE_UPLOD_FILE  = "uploadFile"  //文件上传
	CONFIG_MODE_UPLOD_IMAGE = "uploadImage" //图片上传
)

// 数据类型
const (
	CONFIG_TYPE_STRING = "string" //字符串
	CONFIG_TYPE_INT    = "int"    //整形
	CONFIG_TYPE_UINT   = "uint"   //无符号整形
	CONFIG_TYPE_FLOAT  = "float"  //浮点数
	CONFIG_TYPE_BOOL   = "bool"   //布尔类型
	CONFIG_TYPE_SLICE  = "slice"  //数组类型=相当于Mode=category
	CONFIG_TYPE_OBJECT = "object" //对象类型
)

// 配置结构体
type ConfigStruct struct {
	Field   string          `json:"field"`   //字段名
	Name    string          `json:"name"`    //字段说明
	Desc    string          `json:"desc"`    //字段介绍
	Mode    string          `json:"mode"`    //字段模式 分类 判断 单选 多选 单行 多行
	Type    string          `json:"type"`    //数据类型 string int float bool array
	Options []*ValueOption  `json:"options"` //配置选项
	Childs  []*ConfigStruct `json:"childs"`  //子配置
}

type ValueOption struct {
	Label string      `json:"label"`
	Value interface{} `json:"value"`
	Desc  string      `json:"desc"`
}

// 导出配置结构
func Dump(conf interface{}) []*ConfigStruct {
	st := getStruct(reflect.TypeOf(conf))
	return st.Childs
}

// 获取结构
func getStruct(typ reflect.Type) *ConfigStruct {
	conf := &ConfigStruct{}
	conf.Childs = make([]*ConfigStruct, 0)
	conf.Options = make([]*ValueOption, 0)
	if typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
	}
	switch typ.Kind() {
	case reflect.Struct:
		for i := 0; i < typ.NumField(); i++ {
			f := typ.Field(i)
			name := strings.TrimSpace(f.Tag.Get("name"))
			if name == "" || name == "-" {
				continue
			}
			mode := f.Tag.Get("mode")
			if mode != "" {
				if fn, ok := modesHandle[mode]; ok {
					mode = fn()
				}
				if mode == "none" {
					continue
				}
			}
			c := getStruct(f.Type)
			if f.Type.Kind() == reflect.Struct && name == "extends" {
				conf.Childs = append(conf.Childs, c.Childs...)
				continue
			}
			c.Name = name
			c.Desc = f.Tag.Get("desc")
			c.Field = f.Name
			if c.Mode == "" && mode != "" {
				c.Mode = mode
			}
			if options := f.Tag.Get("options"); options != "" {
				c.Options = makeOptions(c.Type, options)
			}
			conf.Childs = append(conf.Childs, c)
		}
		conf.Type = CONFIG_TYPE_OBJECT
		conf.Mode = CONFIG_MODE_GROUP
	case reflect.Slice:
		t := reflect.SliceOf(typ)
		conf = getStruct(t.Elem().Elem())
		conf.Mode = CONFIG_MODE_LIST
	case reflect.Bool:
		conf.Type = CONFIG_TYPE_BOOL
		conf.Mode = CONFIG_MODE_JUDGE
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		conf.Type = CONFIG_TYPE_INT
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint64, reflect.Uint32:
		conf.Type = CONFIG_TYPE_UINT
	case reflect.String:
		conf.Type = CONFIG_TYPE_STRING
	case reflect.Float32, reflect.Float64:
		conf.Type = CONFIG_TYPE_FLOAT
	}
	return conf
}

var optionsHandle = make(map[string]func() []*ValueOption)

// 注册配置的option，当获取配置的时候调用注册的方法，可用于动态创建options
func RegOptions(optionGroup string, fn func() []*ValueOption) {
	optionsHandle[optionGroup] = fn
}

var modesHandle = make(map[string]func() string)

// 注册mode处理方法，返回新mode
func RegMode(modeName string, fn func() string) {
	modesHandle[modeName] = fn
}

// 创建选项
func makeOptions(typ string, val string) []*ValueOption {
	options := make([]*ValueOption, 0)
	if fn, ok := optionsHandle[val]; ok {
		options = fn()
	} else {
		list := strings.Fields(val)
		for _, item := range list {
			arr := strings.Split(item, ":")
			if len(arr) != 2 {
				try.Throw(0, "tag中的options长度不足2位")
			}
			switch typ {
			case CONFIG_TYPE_INT: //整型
				number, _ := strconv.ParseInt(arr[0], 10, 64)
				options = append(options, &ValueOption{Label: arr[1], Value: number})
			case CONFIG_TYPE_FLOAT: //浮点数
				number, _ := strconv.ParseFloat(arr[0], 64)
				options = append(options, &ValueOption{Label: arr[1], Value: number})
			default: //字符串
				options = append(options, &ValueOption{Label: arr[1], Value: arr[0]})
			}
		}
	}
	return options
}
